#pragma once
#include <iostream>
#include <sys/select.h>
#include "Socket.hpp"
#include "Log.hpp"
#include <poll.h>
#include "InetAddr.hpp"

using namespace hcc;

class PollServer
{
    const static int gnum = sizeof(fd_set) * 8;
    const static int gdefaultfd = -1;

public:
    PollServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
    }
    void InitServer()
    {
        for (int i = 0; i < gnum; i++)
        {
            fd_events[i].fd = gdefaultfd;
            fd_events[i].events = 0;
            fd_events[i].revents = 0;
        }
        fd_events[0].fd=_listensock->Sockfd();
        fd_events[0].events=POLLIN;
    }

    void HandlerIO(int i)
    {
        // 普通的我呢见描述符 ， 正常的读写
        char buffer[1024];
        ssize_t n = ::recv(fd_events[i].fd, buffer, sizeof(buffer) - 1, 0); // 这里的读取不会阻塞，因为select 是等待资源就绪后才执行这个的
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say#: " << buffer << std::endl;
            std::string content = "<html><body><h1>hello bite</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;

            ::send(fd_events[i].fd, echo_str.c_str(), echo_str.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit ... \n");
            // 读取完毕，关闭fd
            ::close(fd_events[i].fd);

            // select 不需要在关心该fd了
            fd_events[i].fd = gdefaultfd;
            fd_events[i].events = 0;
            fd_events[i].revents = 0;
        }
        else
        {
            LOG(ERROR, "recv error\n");
            ::close(fd_events[i].fd);
            fd_events[i].fd = gdefaultfd;
            fd_events[i].events = 0;
            fd_events[i].revents = 0;
        }
    }

    // 处理新连接
    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listensock->Accepter(&addr);
        if (sockfd > 0)
        {
            LOG(DEBUG, "get a new link, client info : %s : %d\n", addr.Ip().c_str(), addr.Port());
            // 获取了连接，但是我们不能读取，因为条件不一定满足
            // 怎样知道fd的数据是否就绪了呢 通过select
            // 想办法添加给select 由select统一进行监管
            // 只要将新的fd 添加到fd_array 数组中即可
            bool flag = false;
            for (int pos = 1; pos < gnum; pos++)
            {
                if (fd_events[pos].fd == gdefaultfd)
                {
                    flag = true;
                    fd_events[pos].fd = sockfd;
                    fd_events[pos].events = POLLIN;
                    LOG(INFO, "add %d to fd_array success ! \n", sockfd);
                    break;
                }
            }
            if (!flag)
            {
                LOG(WARNING, "Server is full \n");
                ::close(sockfd);
            }
        }
    }

    // 一定会存在大量的fd就绪，可能是普通sockfd，也可能是listensockfd
    void HandlerEvent()
    {
        // 事件派发
        for (int i = 0; i < gnum; i++)
        {
            if (fd_events[i].fd == gdefaultfd)
                continue;
            if (fd_events[i].revents&POLLIN)
            {
                // 读事件就绪
                // 1. listensockfd 2. normal sockfd
                if (_listensock->Sockfd() == fd_events[i].fd)
                {
                    Accepter();
                }
                else
                {
                    HandlerIO(i);
                }
            }
        }
    }

    void Loop()
    {
        int timeout = -1;

        while (true)
        {
        
            // _listensock->Accepter();//不能，listensock&&accept 我们也要看作IO类的函数，只管心新连接到来，等价于读事件就绪
            int n = ::poll(fd_events, gnum,timeout);
            switch (n)
            {
            case 0:
                LOG(DEBUG, "time out \n");
                break;
            case -1:
                LOG(ERROR, "select error\n");
                break;
            default:
                LOG(INFO, "haved event ready,n: %d\n", n); // 如果事件就绪，但是不处理，select会一直通知我，知道我处理了
                HandlerEvent();
                // PrintDebug();
                sleep(1);
                break;
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;

    // select 要正常工作，需要借助一个辅助数组，来保存所有合法fd
    struct pollfd fd_events[gnum];
};